<template>
  <div class="model-operation">
    <div ref="carScene" class="car-scene" @mousedown="onMouseDown" @mouseup="onMouseUp" @click="onClickModel"></div>
    <div class="operation-box">
      <div class="color-select">
        <p>选择颜色</p>
        <el-color-picker @change="changeColor"></el-color-picker>
      </div>
      <div class="rotation-control">
        <p>自动旋转</p>
        <el-switch v-model="autoRotate" active-color="#13ce66" @change="changeAutoRotate"> </el-switch>
      </div>
      <div class="image-select">
        <p>选择贴图</p>
        <div class="upload-box">
          <input type="file" class="upload-input" accept="image/*" @change="uploadFile" />
          <img v-show="selectImageFile" class="upload-img" :src="selectImageFile" alt="" />
        </div>
      </div>
    </div>
    <small-popup v-show="popupData.popupVisible" :display-data="popupData.displayData" :left="popupData.left" :top="popupData.top"></small-popup>
  </div>
</template>

<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as TWEEN from "@tweenjs/tween.js";
import { loadCar, loadAircraft } from "@/common/utils/loadModule";
import IndicationPoint from "@/common/utils/sprite";
import smallPopup from "./child/smallPopup.vue";

const $emit = defineEmits(["changeSelectposition"]);
let scene;
let camera;
let renderer;
let controls;

let raycaster;
let mouse;
let selectObject = null;

const state = reactive({
  downTime: 0, //通过鼠标按下，抬起时间，区分点击、长按、拖拽等
  upTime: 0,
  isClick: false,
  currentModule: {
    //当前展示的模型
    group: null,
    bodyMeshList: [],
    bodyColor: "#ba844d" //模型初始的颜色
  },
  autoRotate: false, //自动旋转
  selectImageFile: null,
  cameraPosition: {
    //存储摄像机位置
    x: 0,
    y: 0,
    z: 0
  },
  indicationPoint: null, //创建的指示点
  popupData: {
    popupVisible: false,
    displayData: {},
    left: 0,
    top: 0
  }
});
const { autoRotate, selectImageFile, popupData } = toRefs(state);

onMounted(() => {
  init();
});

const onMouseDown = () => {
  state.downTime = new Date().getTime();
};

const onMouseUp = () => {
  state.upTime = new Date().getTime();
  state.isClick = state.upTime - state.downTime < 300 ? true : false;
};

const onClickModel = (e) => {
  if (!state.isClick) {
    return false;
  }
  // 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
  let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
  let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
  let x = e.clientX + scrollX;
  let y = e.clientY + scrollY;
  mouse.x = (x / e.view.innerWidth) * 2 - 1;
  mouse.y = -(y / e.view.innerHeight) * 2 + 1;
  //通过鼠标点击的位置和当前相机的矩阵计算出射线位置
  raycaster.setFromCamera(mouse, camera);

  // 获取与射线相交的对象数组，按照距离排序，越近的越靠前
  const intersects = raycaster.intersectObjects([...toRaw(state.currentModule.group.children), ...toRaw(state.indicationPoint.group.children)]);
  if (intersects.length > 0) {
    state.popupData.position = intersects[0].point; //存储点击点的位置用于小告示牌显示
    selectObject = intersects[0].object;
    console.log(selectObject.type == "Sprite");
    if (selectObject.type == "Sprite") {
      //选中指示点，移动相机位置
      cameraMove(selectObject.userData);
      state.popupData.popupVisible = false;
    } else {
      //改变选中部件
      $emit("changeSelectposition", selectObject);
      renderDiv();
      state.popupData.popupVisible = true;
    }
  } else {
    state.popupData.popupVisible = false;
    selectObject = null;
  }
};
// 初始化
const init = () => {
  createScene(); // 创建场景
  loadCarOrAircraft("car"); // 加载OBJ模型
  createLight(); // 创建光源
  createCamera(); // 创建相机
  createRender(); // 创建渲染器
  createControls(); // 创建控件对象
  render(); // 渲染
};
// 创建场景
const createScene = () => {
  scene = new THREE.Scene();

  scene.add(new THREE.AxesHelper(20));
  // scene.add(new THREE.GridHelper(10, 10));

  // 声明 raycaster 和 mouse 变量
  raycaster = new THREE.Raycaster();
  mouse = new THREE.Vector2();
};
// 创建光源
const createLight = () => {
  //平行光
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
  directionalLight.position.set(-4, 8, 4);
  directionalLight.castShadow = true;
  //半球光,光源直接放置于场景之上，光照颜色从天空光线颜色颜色渐变到地面光线颜色。
  const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x080820, 0.6);
  hemisphereLight.position.set(0, 8, 0);
  scene.add(directionalLight);
  scene.add(hemisphereLight);
};
// 创建相机
const carScene = ref(null);
const createCamera = () => {
  const element = carScene.value;
  console.log("element.clientWidth", element.clientWidth);
  camera = new THREE.PerspectiveCamera(45, element.clientWidth / element.clientHeight, 0.1, 1000);
  camera.position.set(0, 4.6, 7); // 设置相机位置

  camera.lookAt(scene.position);
};
// 创建渲染器
const createRender = () => {
  const element = carScene.value;
  //antialias - 是否执行抗锯齿。默认为false.  alpha - canvas是否包含alpha (透明度)。默认为 false
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
  renderer.shadowMap.enabled = true; // 显示阴影
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.setClearColor(0x3f3f3f, 1); // 设置背景颜色
  element.appendChild(renderer.domElement);
};
const render = () => {
  TWEEN.update();
  renderer.render(scene, camera);
  requestAnimationFrame(render);
  controls.update();

  if (selectObject != undefined && selectObject != null && selectObject.type != "Sprite") {
    renderDiv();
  }
};
// 创建控件对象
const createControls = () => {
  controls = new OrbitControls(camera, renderer.domElement);
  //启用或禁用相机的缩放
  // controls.enableZoom = false;
  //启用或禁用相机平移
  // controls.enablePan = false;
  //垂直轨道旋转角度
  controls.maxPolarAngle = Math.PI / 2.3;
};
//加载模型
const loadCarOrAircraft = async (type) => {
  if (state.currentModule.group) {
    scene.remove(state.currentModule.group);
    state.currentModule.group = null;
    state.currentModule.bodyMeshList = [];
  }
  let moduleData =
    type == "car"
      ? await loadCar("model/car/car.mtl", "model/car/car.obj")
      : await loadAircraft("model/E-45_Aircraft/E-45_Aircraft.mtl", "model/E-45_Aircraft/E-45_Aircraft.obj");
  state.currentModule.group = moduleData.obj;
  scene.add(moduleData.obj);
  state.currentModule.bodyMeshList = moduleData.bodyMeshList;
  state.currentModule.bodyColor = moduleData.bodyColor;
  // 创建对应模型的指示点
  if (state.indicationPoint) {
    state.indicationPoint.delete();
    state.indicationPoint = null;
  }
  createPoint(type);
};
// 创建指示点
const createPoint = (type) => {
  state.indicationPoint = new IndicationPoint({
    texture: new THREE.TextureLoader().load("/img/hot.png"),
    scene: scene,
    scaleValue: 0.2,
    isadd: false,
    positionAddUserData: [
      {
        position: type == "car" ? [1.5, 1.6, -2.4] : [2.2, 2, -3],
        userData: {
          cameraPosition: type == "car" ? { x: 3.8, y: 2, z: -6 } : { x: 4.3, y: 2.3, z: -6 }
        }
      },
      {
        position: [-1.1, 1.4, -0.2],
        userData: {
          cameraPosition: { x: -5, y: 3, z: -2 }
        }
      },
      {
        position: [0.8, 1.4, 1],
        userData: {
          cameraPosition: { x: 6, y: 2.5, z: 4.8 }
        }
      }
    ]
  });
};
//点击指示点时，相机移动到对应方位
const cameraMove = (userData) => {
  state.cameraPosition.x = camera.position.x;
  state.cameraPosition.y = camera.position.y;
  state.cameraPosition.z = camera.position.z;
  const tween = new TWEEN.Tween(state.cameraPosition).to(userData.cameraPosition, 3000);
  tween.easing(TWEEN.Easing.Sinusoidal.InOut);
  console.log(camera.position);

  tween.onUpdate(() => {
    camera.position.set(state.cameraPosition.x, state.cameraPosition.y, state.cameraPosition.z);
  });
  tween.start();
};
//修改模型颜色
const changeColor = (val) => {
  console.log(val);
  let childrenArr = state.currentModule.bodyMeshList;
  for (let i = 0; i < childrenArr.length; i++) {
    if (Array.isArray(childrenArr[i].material)) {
      for (let j = 0; j < childrenArr[i].material.length; j++) {
        //判断mesh中材质颜色和当前外壳颜色一致的才修改，因为里面混杂了玻璃、标志等的材质，需要过滤
        if (childrenArr[i].material[j].color.getHexString() == state.currentModule.bodyColor) {
          childrenArr[i].material[j].color.setStyle(val);
        } else {
          console.log("test", state.currentModule.bodyColor, childrenArr[i].material[j].color.getHexString());
        }
      }
    } else {
      if (childrenArr[i].material.color.getHexString() == state.currentModule.bodyColor) {
        childrenArr[i].material.color.setStyle(val);
      }
    }
  }
  // 更新当前颜色
  state.currentModule.bodyColor = val.slice(1).toLowerCase();
};
//自动旋转
const changeAutoRotate = (value) => {
  controls.autoRotate = value;
};
// 更新模型小告示牌的位置
const renderDiv = () => {
  // 获取窗口的一半高度和宽度
  var halfWidth = window.innerWidth / 2;
  var halfHeight = window.innerHeight / 2;

  // 逆转相机求出二维坐标
  var vector = state.popupData.position.clone().project(camera);

  // 显示模型信息
  state.popupData.left = vector.x * halfWidth + halfWidth;
  state.popupData.top = -vector.y * halfHeight + halfHeight - state.popupData.position.y;
  state.popupData.displayData.name = selectObject.name;
};
const uploadFile = (e) => {
  let file = e.target.files[0];
  var url = null;
  if (window.createObjectURL != undefined) {
    // basic
    url = window.createObjectURL(file);
  } else if (window.URL != undefined) {
    // mozilla(firefox)
    url = window.URL.createObjectURL(file);
  } else if (window.webkitURL != undefined) {
    // webkit or chrome
    url = window.webkitURL.createObjectURL(file);
  }
  state.selectImageFile = url;

  if (selectObject) {
    let currentMaterial = Array.isArray(selectObject.material) ? selectObject.material[0] : selectObject.material;

    let texture = new THREE.TextureLoader().load(url);
    currentMaterial.map = texture;
    currentMaterial.needsUpdate = true;
  }
};

defineExpose({ loadCarOrAircraft });
</script>
<style lang="scss" scoped>
.model-operation {
  width: 100%;
  height: 100%;
  position: relative;
  .car-scene {
    width: 100%;
    height: 100%;
  }
  .operation-box {
    width: 200px;
    height: 90px;
    padding: 10px;
    border-radius: 3px;
    box-shadow: 0 0 10px 3px rgba(103, 253, 252, 0.4) inset;
    background-color: #02161d;
    display: flex;
    justify-content: space-around;
    position: absolute;
    bottom: 40px;
    left: 50%;
    transform: translateX(-50%);
    p {
      font-size: 14px;
      color: #65faf9;

      text-align: center;
      margin-bottom: 6px;
    }
  }
}
</style>
